package distribution;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import dto.Sample;

public class MixtureOfGaussianDistribution implements Distribution {

	private List<Distribution> gaussians;
	private List<Double> priors;
	
	@Override
	public double getLogProb(Sample sample) {
		List<Double> logProbs = new ArrayList<Double>(gaussians.size());
		
		for(Distribution dist : gaussians) {
			logProbs.add(dist.getLogProb(sample));
		}
		
		List<Double> weightedLogProbs = new ArrayList<Double>(logProbs.size());
		Iterator<Double> itr = priors.iterator();
		for(double logProb : logProbs) {
			double logw = Math.log(itr.next());
			weightedLogProbs.add(logw + logProb);
		}
		
		// for simplification -> nearest neighbor.
		double logSum = getLogSum(weightedLogProbs);
		
		return logSum;
	}
	
	protected double getLogSum(List<Double> list) {
		double logk = 1 - Collections.max(list);
		double sumkxi = 0;
		for(double logxi : list) {
			sumkxi += Math.exp(logk + logxi);
		}
		double logsumkxi = Math.log(sumkxi);
		double logsumxi = logsumkxi - logk;
		return logsumxi;
	}

	@Override
	public void train(List<List<Sample>> samples, int dim) {
		gaussians = new ArrayList<Distribution>(samples.size());
		priors = new ArrayList<Double>(samples.size());
		
		int all = 0;
		for(List<Sample> s : samples) {
			all += s.size();
		}
		
		for(List<Sample> s : samples) {
			priors.add(s.size() / (double) all);
			Distribution d = new GaussianDistribution();
			List<List<Sample>> ss = new ArrayList<List<Sample>>(1);
			ss.add(s);
			d.train(ss, dim);
			gaussians.add(d);
		}
	}

}
